package com.redfootdev.horseskills;

import java.util.List;

import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Horse;
import org.bukkit.entity.Player;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;

import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import com.redfootdev.horseskills.Agility;
import com.redfootdev.horseskills.Trample;
import com.redfootdev.horseskills.Strength;
import com.redfootdev.horseskills.HorseSkills;
import com.redfootdev.horseskills.HorseDatabase;

import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;

public class HorseEffects {
	
	private JavaPlugin plugin;
	private HorseDatabase hdb;
	private PlayerDatabase pdb;
	private Trample trample;
	private Strength strength;
	private Agility agility;
	private Intelligence intelligence;
	private Attractiveness attractiveness;
	
	public HorseEffects(JavaPlugin plugin, HorseDatabase hdb, PlayerDatabase pdb) {
		
		this.plugin = plugin;
		this.hdb = hdb;
		this.pdb = pdb;
		trample = new Trample();
		strength = new Strength();
		agility = new Agility();
		intelligence = new Intelligence();
		attractiveness = new Attractiveness();
	}
	
	
	// horseHitsomething() - This is the top level routine that is called
	// 						by the event handler for a horse colliding in to something.
	// 						This routine expects that the caller made sure that it was 
	//						a ridden horse that hit something. If this routine is called, 
	//						we should have already noticed the horse is being ridden and 
	//						it should have been already added to the horse database. 
	//						We also assume the player is in the player database.
	//
	public void horseHitSomething(Entity horse, Player rider, Entity victim) {
		int xpIncrease = trample.horseTrampledSomething( hdb,  pdb,  horse,  rider,  victim);
		updateHorsemanshipAndHandling(horse, rider, xpIncrease);
	}
	
	public void horseHasDied(Entity horse) {
		hdb.horseDied(horse);
	}
	public boolean horseJumped(Horse horse, Player rider, float jumpPower) {
		//
		//TODO: Make different levels of weight so that a heavily burdened horse can't jump but can still move around.
		if(!strength.HorseCanCarryWeight(hdb, pdb, horse, rider, calcWeight(rider))) {
			return false;
		}
		
		int xpIncrease = agility.horseJumped(hdb, pdb, horse, rider, jumpPower);
		updateHorsemanshipAndHandling(horse, rider, xpIncrease);
		
		return true;
	}
	public double horseFell(Entity horse, Player rider, double fallDamage) {
		
		int xpIncrease = agility.horseHasFallen(hdb, pdb, horse, rider, fallDamage);
		updateHorsemanshipAndHandling(horse, rider, xpIncrease);
		// Reduce fall damage based on agility level.
		double fallDamageReduction = agility.fallDamageReduction(hdb, (Horse)horse, fallDamage);
		
		return fallDamageReduction;
	}
	public void horseWasHitBySomething(Entity horse,Player rider, int damage) {
		int xpIncrease = strength.horseWasHitBySomething(hdb, pdb, horse, rider, damage);
		updateHorsemanshipAndHandling(horse, rider, xpIncrease);
	}
	public boolean horseMounted (Entity horse, Player rider) {
		
		//
		// Strength Skill Stuff
		if(!strength.HorseCanCarryWeight(hdb, pdb, horse, rider, calcWeight(rider))) {
			return false;
		}
		
		//
		// Horse Time Mounted
		hdb.setTimeWhenMounted(horse, System.currentTimeMillis());
		
		return true;
	}
	public void horseDismounted (Entity horse, Player rider) {
		
		long timeWhenMounted = hdb.getTimeWhenMounted(horse);
		int timeRidden = (int)((System.currentTimeMillis() - timeWhenMounted)/1000);
		
		int xpIncreaseStrength = strength.HorseWasRiddenForTime(hdb, pdb, horse, rider, timeRidden);
		int xpIncreaseAgility = agility.horseWasRiddenForTime(hdb, pdb, horse, rider, timeRidden);
		updateHorsemanshipAndHandling(horse, rider, xpIncreaseStrength);
		updateHorsemanshipAndHandling(horse, rider, xpIncreaseAgility);
		hdb.setTimeWhenMounted(horse, 0);
		//Gain strength XP based on the amount of time, and the weight of items you are carrying.
		//
	}
	
	public int getWeight(Material m) {
		
		switch (m) {
		case APPLE:	return 1;
		case CAKE:	return 1;
		default:	return 1;
		}
		
	}
	
	public void updateHorsemanshipAndHandling(Entity horse, Player rider, int xpIncrease) {

		Skill handling = hdb.getHandling(horse, rider);
		Skill horsemanship = pdb.getHorsemanship(rider);
		// calculate XP gain for handling
		
		int xpGainHandling = (int)(xpIncrease*0.2);
		
		// calculate XP gain for horsemanship.
		int xpGainHorsemanship = (int) (xpGainHandling*0.5);
		
		// apply XP to handling Skill
		if(handling.addXP(xpGainHandling)) {
			rider.sendMessage(ChatColor.AQUA + "Your Handling level is now " + handling.getLevel() + ".");
		}
		hdb.setHandling(horse, rider, handling);
		
		// apply XP to horsemanship Skill
		if(horsemanship.addXP(xpGainHorsemanship)) {
			rider.sendMessage(ChatColor.AQUA + "Your Horsemanship level is now " + horsemanship.getLevel() + ".");
		}
		pdb.setHorsemanship(rider, horsemanship);
	}
	
	public boolean horseInteractedWith(Entity horse, Player rider) {
		
		rider.getItemInHand().setAmount(rider.getItemInHand().getAmount()-1);
		
		if(rider.getItemInHand().getType() == Material.CARROT_ITEM){
			int xpIncrease = intelligence.horseWasFedSomething(hdb, pdb, horse, rider, rider.getItemInHand());
			updateHorsemanshipAndHandling(horse, rider, xpIncrease);
			return true;
		}
		if(rider.getItemInHand().getType() == Material.SHEARS && (System.currentTimeMillis() - hdb.getTimeWhenGroomed(horse))/1000 >= 3600  ) {
			rider.playSound(rider.getLocation(), Sound.SHEEP_SHEAR, 10, 1);
			rider.sendMessage(ChatColor.AQUA + "You have groomed your horse!");
			int xpIncrease = attractiveness.horseWasGroomed(hdb, pdb, horse, rider);
			updateHorsemanshipAndHandling(horse, rider, xpIncrease);
			hdb.setTimeWhenGroomed(horse, System.currentTimeMillis());
			return true;
		}
		if(rider.getItemInHand().getType() == Material.WATER_BUCKET) {
			rider.playSound(rider.getLocation(), Sound.WATER, 10, 1);
			rider.sendMessage(ChatColor.AQUA + "You have washed your horse!");
			int xpIncrease = attractiveness.horseWasWashed(hdb, pdb, horse, rider);
			updateHorsemanshipAndHandling(horse, rider, xpIncrease);
			return true;
		}
				
		return false;
	}
	// playerRightClicked() - Checks for special interaction events from  player.
	public boolean playerRightClicked(Player rider) {
		//Check perform the grass whistle event
		if(rider.getItemInHand().getType() == Material.GRASS) {
			rider.playSound(rider.getLocation(), Sound.SHOOT_ARROW, 10, 1);
			List<Entity> stuff =  rider.getPlayer().getNearbyEntities(110, 110, 110);
			for (Entity target: stuff) {
				if(target.getType() == EntityType.HORSE){
					int xpIncrease = intelligence.horseWhistledFor(hdb, pdb, target, rider);
					updateHorsemanshipAndHandling(target, rider, xpIncrease);
				}
			}
			return true;
		}
		return false;
	}
	// playerTeleportNextToHorse() - Teleports a horse with a player if the horse is 
	//									next to the player when he teleports. Only works
	//									on horses the owner has a Handling skill with.
	public void playerTeleportedNextToHorse(Player rider, Horse horse, Location to) {
		if(hdb.hasHandling(horse, rider)) {
			horse.teleport(to.add(1, 0, 1));
		}
		
		
	}
	// calcWeight() - Calculates the weight a player is carrying.
	public int calcWeight(Player rider) {
		
		int weight = 0;
		
		ItemStack stacks[] = rider.getInventory().getContents();
		ItemStack armorStacks[] = rider.getInventory().getArmorContents();
		// get the horses armor
		//ItemStack harmorStacks[] = 
		ItemStack harmorStacks[] = null;
		
		// for each, enumerate the item stacks and calculate the weight.
		if (stacks != null){
			for(int i = 0; i < stacks.length; i++) {
				if( stacks[i] != null){
					weight += stacks[i].getAmount()*getWeight(stacks[i].getType());	
				}
				
			}
		}
		if (armorStacks != null){
			for(int i = 0; i < armorStacks.length; i++) {
				if( armorStacks[i] != null){
					weight += armorStacks[i].getAmount()*getWeight(armorStacks[i].getType());
				}
					
			}
		}
		if (harmorStacks != null){
			for(int i = 0; i < harmorStacks.length; i++) {
				if( harmorStacks[i] != null){
					weight += harmorStacks[i].getAmount()*getWeight(harmorStacks[i].getType());	
				}
			}
		}
		
		return weight;
	}
	//
	// isconsumableItem - Tell us whether or not we will be subtracting one 
	//						from the Item stack upon performing the action.
	//
	public boolean isConsumableItem(ItemStack item) {
		boolean consumable;
		Material material = item.getType();
		consumable = material == Material.CARROT_ITEM;
					 
		if(consumable){
			return true;
		}
		return false;
	}

}
